home *** CD-ROM | disk | FTP | other *** search
/ SGI Freeware 2002 November / SGI Freeware 2002 November - Disc 2.iso / dist / fw_glimpse.idb / usr / freeware / src / glimpse-3.0 / compress / cast.c.z / cast.c
C/C++ Source or Header  |  1997-09-09  |  23KB  |  836 lines

  1. /* Copyright (c) 1994 Burra Gopal, Udi Manber.  All Rights Reserved. */
  2.  
  3. /*
  4.  * cast.c:    main text compression routines. Exports tcompress() called
  5.  *        from main() in read_out.c, and one other simple routine
  6.  *        tcompressible_file(). This module can also be used from csearch.c.
  7.  */
  8.  
  9. #include "defs.h"
  10. #include <sys/time.h>
  11. #if defined(__NeXT__)
  12.                                       /* NeXT has no <utime.h> */
  13. struct utimbuf {
  14.         time_t actime;          /* access time */
  15.         time_t modtime;         /* modification time */
  16. };
  17. #else
  18. #include <utime.h>
  19. #endif
  20. #define ALNUMWORDS 1
  21.  
  22. #define MYEOF    0xffffffff
  23.  
  24. extern int RESERVED_CHARS;
  25. extern int MAX_WORDS;
  26. extern int SPECIAL_WORDS;
  27. extern int BEGIN_SPECIAL_WORDS;
  28. extern int END_SPECIAL_WORDS;
  29. extern int NUM_SPECIAL_DELIMITERS;
  30. extern int END_SPECIAL_DELIMITERS;
  31. extern int ONE_VERBATIM;
  32. extern int next_free_hash, next_free_str;
  33. extern hash_entry freq_words_table[MAX_WORD_LEN+2][256];        /* 256 is the maximum possible number of special words */
  34. extern char freq_words_strings[256][MAX_WORD_LEN+2];
  35. extern int freq_words_lens[256];
  36. extern char comp_signature[SIGNATURE_LEN];
  37. extern hash_entry *compress_hash_table[HASH_TABLE_SIZE];
  38.  
  39. extern int usemalloc;
  40.  
  41. /* initialize and load dictionaries */
  42. initialize_tcompress(hash_file, freq_file, flags)
  43.     char    *hash_file, *freq_file;
  44.     int    flags;
  45. {
  46.     FILE    *hashfp;
  47.  
  48.     if (!initialize_common(freq_file, flags)) return 0;
  49.     next_free_hash = 0;
  50.     memset(compress_hash_table, '\0', sizeof(hash_entry *) * HASH_TABLE_SIZE);
  51.         if (MAX_WORDS == 0) return 1;
  52.  
  53.     /* Load compress dictionary */
  54.     if ((hashfp = fopen(hash_file, "r")) == NULL) {
  55.         if (flags & TC_ERRORMSGS) {
  56.             fprintf(stderr, "cannot open cast-dictionary file: %s\n", hash_file);
  57.             fprintf(stderr, "(use -H to give a dictionary-dir or run 'buildcast' to make a dictionary)\n");
  58.         }
  59.         return 0;
  60.     }
  61.     if (!tbuild_hash(compress_hash_table, hashfp, -1)) {    /* read all bytes until end */
  62.         fclose(hashfp);
  63.         return 0;
  64.     }
  65.     fclose(hashfp);
  66.     return 1;
  67. }
  68.  
  69. uninitialize_tcompress()
  70. {
  71.     int    i;
  72.     hash_entry *e, *t;
  73.  
  74.     uninitialize_common();
  75.     if (usemalloc) {
  76.         for (i=0; i<HASH_TABLE_SIZE; i++) {
  77.             e = compress_hash_table[i];
  78.             while (e != NULL) {
  79.                 t = e;
  80.                 e = e->next;
  81.                 free(t->word);
  82.                 free(t);
  83.             }
  84.         }
  85.     }
  86.     memset(compress_hash_table, '\0', sizeof(hash_entry *) * HASH_TABLE_SIZE);
  87.     next_free_hash = next_free_str = 0;
  88. }
  89.  
  90. /* TRUE if input file has been compressed already, FALSE otherwise */
  91. int
  92. already_tcompressed(buffer, length, flags)
  93.     char    *buffer;
  94.     int    length;
  95.     int    flags;
  96. {
  97.     char    *sig = comp_signature;
  98.  
  99.     if (!strncmp(buffer, sig, SIGNATURE_LEN - 1)) {
  100.         if (flags & TC_ERRORMSGS)
  101.             fprintf(stderr, "Already compressed,");
  102.         return 1;
  103.     }
  104.     return 0;
  105. }
  106.  
  107. extern int initialize_common_done;
  108.  
  109. /* TRUE if input file is an ascii file, FALSE otherwise */
  110. int
  111. tcompressible(buffer, num_read, flags)
  112.     char    *buffer;
  113.     int    *num_read;
  114.     int    flags;
  115. {
  116.     if (!initialize_common_done) {
  117.         if (flags & TC_ERRORMSGS)
  118.             fprintf(stderr, "No cast-dictionary,");
  119.         return 0;
  120.     }
  121.         if(ttest_binary(buffer, num_read)) {
  122.         if (flags & TC_ERRORMSGS)
  123.             fprintf(stderr, "Binary data,");
  124.         return(0);
  125.     }
  126.         if(ttest_uuencode(buffer, num_read)) {
  127.         if (flags & TC_ERRORMSGS)
  128.             fprintf(stderr, "UUEncoded data,");
  129.         return(0);
  130.     }
  131.         if(ttest_postscript(buffer, num_read)) {
  132.         if (flags & TC_ERRORMSGS)
  133.             fprintf(stderr, "Postscript data,");
  134.         return(0);
  135.     }
  136.     if (already_tcompressed(buffer, num_read)) return 0;
  137.     return(1);
  138. }
  139.  
  140. tcompressible_file(name, flags)
  141.     char    *name;
  142.     int    flags;
  143. {
  144.     char    buf[SAMPLE_SIZE + 2];
  145.     int    num;
  146.     FILE    *fp = fopen(name, "r");
  147.  
  148.     if (!initialize_common_done) {
  149.         if (flags & TC_ERRORMSGS)
  150.             fprintf(stderr, "No cast-dictionary,");
  151.         if (fp != NULL) fclose(fp);
  152.         return 0;
  153.     }
  154.     if (fp == NULL) return 0;
  155.     num = fread(buf, 1, SAMPLE_SIZE, fp);
  156.     fclose(fp);
  157.     return(tcompressible(buf, num, flags));
  158. }
  159.  
  160. tcompressible_fp(fp, flags)
  161.     FILE    *fp;
  162.     int    flags;
  163. {
  164.     char    buf[SAMPLE_SIZE + 2];
  165.     int    num;
  166.  
  167.     if (!initialize_common_done) {
  168.         if (flags & TC_ERRORMSGS)
  169.             fprintf(stderr, "No cast-dictionary,");
  170.         return 0;
  171.     }
  172.     if (fp == stdin) return 1;
  173.     num = fread(buf, 1, SAMPLE_SIZE, fp);
  174.     return(tcompressible(buf, num, flags));
  175. }
  176.  
  177. /* -------------------------------------------------------------------------
  178. tgetword():
  179. get a word from stream pointed to by fp: a "word" is an ID = a stream of
  180. alphanumeric characters beginning with an alphanumeric char and ending with
  181. a non-alphanumeric character. The character following the word is returned,
  182. and the file pointer points to THIS character in the input stream. If there
  183. is no word beginning at the current position of the file pointer, tgetword
  184. simply behaves like getc(), i.e., just returns the character read. If the
  185. word is too long, then it fills up all the bytes it can and returns the
  186. character it could not fill up.
  187.  
  188. To read a series of words without doing an ungetc() for the extra character
  189. read by tgetword, the caller can set *length to 1 and word[0] to the character
  190. returned by tgetword. This can make compress work even if infile = stdin.
  191. --------------------------------------------------------------------------*/
  192. unsigned int
  193. tgetword(fp, buf, maxinlen, lenp, word, length)
  194.     FILE    *fp;
  195.     char    *buf;
  196.     int    maxinlen;
  197.     int    *lenp;
  198.     char    *word;
  199.     int    *length;
  200. {
  201.     unsigned int    c;
  202.  
  203. #if    !ALNUMWORDS
  204.     if (*length > 0){
  205.         c = word[*length - 1];
  206.         if (!isalpha(c)) goto not_alpha;
  207.         else goto alpha;
  208.     }
  209.  
  210.     if ((c = mygetc(fp, buf, maxinlen, lenp)) == MYEOF) return MYEOF;
  211.     if (!isalpha(c)) {    /* this might be a number */
  212.         if (!isdigit(c)) return c;
  213.         word[*length] = c;
  214.         (*length) ++;
  215.         word[*length] = '\0';
  216.     not_alpha:
  217.         while(isdigit(c = mygetc(fp, buf, maxinlen, lenp))) {
  218.             if (*length >= MAX_NAME_LEN) return c;
  219.             word[*length] = c;
  220.             (*length) ++;
  221.             word[*length] = '\0';
  222.         }
  223.         return c;
  224.     }
  225.     else {    /* this might be a dictionary word */
  226.         word[*length] = c;
  227.         (*length) ++;
  228.         word[*length] = '\0';
  229.     alpha:
  230.         while(isalnum(c = mygetc(fp, buf, maxinlen, lenp))) {
  231.             if (*length >= MAX_NAME_LEN) return c;
  232.             word[*length] = c;
  233.             (*length) ++;
  234.             word[*length] = '\0';
  235.         }
  236.         return c;
  237.     }
  238. #else    /*!ALNUMWORDS*/
  239.     if (*length > 0){
  240.         c = word[*length - 1];
  241.     }
  242.     else {
  243.         if ((c = mygetc(fp, buf, maxinlen, lenp)) == MYEOF) return MYEOF;
  244.         if (!isalnum(c)) return c;
  245.         word[(*length)++] = c;
  246.         word[*length] = '\0';
  247.     }
  248.     while(((c = mygetc(fp, buf, maxinlen, lenp)) != MYEOF) && (isalnum(c))) {
  249.         if (*length >= MAX_NAME_LEN) return c;
  250.         word[*length] = c;
  251.         (*length) ++;
  252.         word[*length] = '\0';
  253.     }
  254.     return c;
  255. #endif    /*!ALNUMWORDS*/
  256. }
  257.  
  258. /*--------------------------------------------------------------------
  259. Skips a series of characters of the type skipc and sets the number of
  260. characters skipped. Used to compress multiple blanks, tabs & newlines.
  261. It returns the first character not equal to skipc. If there are no
  262. characters beginning at the current location of the file pointer
  263. which are equal to skipc, this function simply behaves as getc().
  264. ---------------------------------------------------------------------*/
  265. int
  266. skip(fp, buf, maxinlen, lenp, skipc, skiplen)
  267.     FILE    *fp;
  268.     char    *buf;
  269.     int    maxinlen;
  270.     int    *lenp;
  271.     int    skipc;
  272.     int    *skiplen;
  273. {
  274.     unsigned int    c;
  275.  
  276.     *skiplen = 1;    /* c has already been read! */
  277.     while((c = mygetc(fp, buf, maxinlen, lenp)) == skipc) (*skiplen) ++;
  278.     return c;
  279. }
  280.  
  281. /* defined in misc.c */
  282. extern char special_texts[];
  283. extern char special_delimiters[];
  284.  
  285. int
  286. get_special_text_index(c)
  287.     unsigned int    c;
  288. {
  289.     int    i;
  290.  
  291.     for(i=0; i<NUM_SPECIAL_TEXTS; i++)
  292.         if (special_texts[i] == c) return i;
  293.     return -1;
  294. }
  295.  
  296. int
  297. get_special_delimiter_index(c)
  298.     unsigned int    c;
  299. {
  300.     int    i;
  301.  
  302.     for(i=0; i<NUM_SPECIAL_DELIMITERS; i++)
  303.         if (special_delimiters[i] == c) return i;
  304.     return -1;
  305. }
  306.  
  307. #define process_spaces(skipc, skiplen)\
  308. {\
  309.     int    count2 = 0, count1 = 0, i;\
  310. \
  311.     count2 = (skiplen/2);\
  312.     count1 = (skiplen%2);\
  313. \
  314.     if (easysearch) {\
  315.         switch(skipc)\
  316.         {\
  317.         case ' ':\
  318.             if ((maxoutlen >= 0) && (outlen + count1 + count2 >= maxoutlen)) return outlen;\
  319.             if (outfp != NULL) {\
  320.                 for (i=0; i<count2; i++) putc(TWOBLANKS, outfp);\
  321.                 for (i=0; i<count1; i++) putc(BLANK, outfp);\
  322.                 outlen += count1 + count2;\
  323.             }\
  324.             if (outbuf != NULL) {\
  325.                 for (i=0; i<count2; i++) outbuf[outlen ++] = TWOBLANKS;\
  326.                 for (i=0; i<count1; i++) outbuf[outlen ++] = BLANK;\
  327.             }\
  328.             break;\
  329. \
  330.         case '\t':\
  331.             if ((maxoutlen >= 0) && (outlen + count1 + count2 >= maxoutlen)) return outlen;\
  332.             if (outfp != NULL) {\
  333.                 for (i=0; i<count2; i++) putc(TWOTABS, outfp);\
  334.                 for (i=0; i<count1; i++) putc(TAB, outfp);\
  335.                 outlen += count1 + count2;\
  336.             }\
  337.             if (outbuf != NULL) {\
  338.                 for (i=0; i<count2; i++) outbuf[outlen ++] = TWOTABS;\
  339.                 for (i=0; i<count1; i++) outbuf[outlen ++] = TAB;\
  340.             }\
  341.             break;\
  342. \
  343.         case '\n':\
  344.             if ((maxoutlen >= 0) && (outlen + count1 + count2*2 >= maxoutlen)) return outlen;\
  345.             if (outfp != NULL) {\
  346.                 for (i=0; i<count2*2; i++) putc(NEWLINE, outfp);\
  347.                 for (i=0; i<count1; i++) putc(NEWLINE, outfp);\
  348.                 outlen += count1 + count2*2;\
  349.             }\
  350.             if (outbuf != NULL) {\
  351.                 for (i=0; i<count2*2; i++) outbuf[outlen ++] = NEWLINE;\
  352.                 for (i=0; i<count1; i++) outbuf[outlen ++] = NEWLINE;\
  353.             }\
  354.             break;\
  355. \
  356.         default: break;    /* cannot reach here */\
  357.         }\
  358.     }\
  359.     else {\
  360.         if ((maxoutlen >= 0) && (outlen + count1 + count2 >= maxoutlen)) return outlen;\
  361.         switch(skipc)\
  362.         {\
  363.         case ' ':\
  364.             if (outfp != NULL) {\
  365.                 for (i=0; i<count2; i++) putc(TWOBLANKS, outfp);\
  366.                 for (i=0; i<count1; i++) putc(BLANK, outfp);\
  367.                 outlen += count1 + count2;\
  368.             }\
  369.             if (outbuf != NULL) {\
  370.                 for (i=0; i<count2; i++) outbuf[outlen ++] = TWOBLANKS;\
  371.                 for (i=0; i<count1; i++) outbuf[outlen ++] = BLANK;\
  372.             }\
  373.             break;\
  374. \
  375.         case '\t':\
  376.             if (outfp != NULL) {\
  377.                 for (i=0; i<count2; i++) putc(TWOTABS, outfp);\
  378.                 for (i=0; i<count1; i++) putc(TAB, outfp);\
  379.                 outlen += count1 + count2;\
  380.             }\
  381.             if (outbuf != NULL) {\
  382.                 for (i=0; i<count2; i++) outbuf[outlen ++] = TWOTABS;\
  383.                 for (i=0; i<count1; i++) outbuf[outlen ++] = TAB;\
  384.             }\
  385.             break;\
  386. \
  387.         case '\n':\
  388.             if (outfp != NULL) {\
  389.                 for (i=0; i<count2; i++) putc(TWONEWLINES, outfp);\
  390.                 for (i=0; i<count1; i++) putc(NEWLINE, outfp);\
  391.                 outlen += count1 + count2;\
  392.             }\
  393.             if (outbuf != NULL) {\
  394.                 for (i=0; i<count2; i++) outbuf[outlen ++] = TWONEWLINES;\
  395.                 for (i=0; i<count1; i++) outbuf[outlen ++] = NEWLINE;\
  396.             }\
  397.             break;\
  398. \
  399.         default: break;    /* cannot reach here */\
  400.         }\
  401.     }\
  402. }
  403.  
  404. #define PRE_VERBATIM(v)\
  405. {\
  406.     if (!v) {\
  407.         v = 1;\
  408.         if ((maxoutlen >= 0) && (outlen + 1 >= maxoutlen)) return outlen;\
  409.         if (outfp != NULL) putc(BEGIN_VERBATIM, outfp);\
  410.         if (outbuf != NULL) outbuf[outlen] = BEGIN_VERBATIM;\
  411.         outlen ++;\
  412.     }\
  413. }
  414.  
  415. #define POST_VERBATIM(v) \
  416. {\
  417.     if (v) {\
  418.         v = 0;\
  419.         if ((maxoutlen >= 0) && (outlen + 1 >= maxoutlen)) return outlen;\
  420.         if (outfp != NULL) putc(END_VERBATIM, outfp);\
  421.         if (outbuf != NULL) outbuf[outlen] = END_VERBATIM;\
  422.         outlen ++;\
  423.     }\
  424. }
  425.  
  426. #define EASY_PRE_VERBATIM(v) \
  427. {\
  428.     if (easysearch) {\
  429.         if ((maxoutlen >= 0) && (outlen + 1 >= maxoutlen)) return outlen;\
  430.         if (outfp != NULL) putc(ONE_VERBATIM, outfp);\
  431.         if (outbuf != NULL) outbuf[outlen] = ONE_VERBATIM;\
  432.         outlen ++;\
  433.     }\
  434.     else {\
  435.         PRE_VERBATIM(v)\
  436.     }\
  437. }
  438.  
  439. #define EASY_POST_VERBATIM(v) \
  440. {\
  441.     if (easysearch) {\
  442.         POST_VERBATIM(v)\
  443.     }\
  444.     /* else ignore */\
  445. }
  446.  
  447. int
  448. get_special_word_index(word, len)
  449.     char    word[MAX_NAME_LEN];
  450.     int    len;
  451. {
  452.     register int    comp;
  453.     hash_entry    *e;
  454.  
  455.     if ((len > MAX_WORD_LEN) || (SPECIAL_WORDS <= 0)) return -1;
  456.     e = freq_words_table[len];
  457.     while((e != NULL) && (e->val.offset != -1)) {
  458.         comp = strcmp(word, e->word);
  459.         if (comp == 0) return e->val.offset;
  460.         if (comp < 0) return -1;    /* can't find it anyway */
  461.         e = e->next;
  462.     }
  463.     return -1;
  464. }
  465.  
  466. /* Compresses input from indata and outputs it into outdata: returns number of chars in output */
  467. int
  468. tcompress(indata, maxinlen, outdata, maxoutlen, flags)
  469.     void    *indata, *outdata;
  470.     int    maxinlen, maxoutlen;
  471.     int    flags;
  472. {
  473.     unsigned char    curword[MAX_NAME_LEN];
  474.     int    curlen;
  475.     int    hashindex;
  476.     hash_entry *e;
  477.     unsigned int    c;
  478.     unsigned short    encodedindex;
  479.     int    skiplen;
  480.     int    ret;
  481.     int    verbatim_state = 0;
  482.     char    *sig = comp_signature;
  483.     FILE    *infp = NULL, *outfp = NULL;
  484.     unsigned char    *inbuf = NULL, *outbuf = NULL;
  485.     int    outlen = 0, inlen = 0;
  486.     int    easysearch = flags&TC_EASYSEARCH;
  487.     int    untilnewline = flags&TC_UNTILNEWLINE;
  488.  
  489.     if (flags & TC_SILENT) return 0;
  490.     if (easysearch) {
  491.         ONE_VERBATIM = EASY_ONE_VERBATIM;
  492.         NUM_SPECIAL_DELIMITERS = EASY_NUM_SPECIAL_DELIMITERS;
  493.         END_SPECIAL_DELIMITERS = EASY_END_SPECIAL_DELIMITERS;
  494.     }
  495.     else {
  496.         ONE_VERBATIM = HARD_ONE_VERBATIM;
  497.         NUM_SPECIAL_DELIMITERS = HARD_NUM_SPECIAL_DELIMITERS;
  498.         END_SPECIAL_DELIMITERS = HARD_END_SPECIAL_DELIMITERS;
  499.     }
  500.  
  501.     if (maxinlen < 0) {
  502.         infp = (FILE *)indata;
  503.     }
  504.     else {
  505.         inbuf = (unsigned char *)indata;
  506.     }
  507.     if (maxoutlen < 0) {
  508.         outfp = (FILE *)outdata;
  509.     }
  510.     else {
  511.         outbuf = (unsigned char *)outdata;
  512.     }
  513.  
  514.     /* Write signature and information about whether compression was context-free or not: first 16 bytes */
  515.     if (outfp != NULL) {
  516.         if ((maxoutlen >= 0) && (outlen + SIGNATURE_LEN >= maxoutlen)) return outlen;
  517.         if (0 == fwrite(sig, 1, SIGNATURE_LEN - 1, outfp)) return 0;
  518.         if (easysearch) putc(1, outfp);
  519.         else putc(0, outfp);
  520.         outlen += SIGNATURE_LEN;
  521.     }
  522.     /* No need to put a signature OR easysearch when doing it in memory: caller must manipulate */
  523.  
  524.     /*
  525.      * The algorithm for compression is as follows:
  526.      *
  527.      * For each input word, we search and see if it is in the dictionary.
  528.      * If it IS there, we just look at its word-index and output it.
  529.      * Then, if the character immediately after the word is NOT a blank,
  530.      * we output a second character indicating what it was.
  531.      *
  532.      * If it is not in the dictionary then we output it verbatim: for
  533.      * verbatim o/p, we take care to merge consecutive verbatim outputs
  534.      * by NOT putting delimiters between them (one start and one end
  535.      * delimiter).
  536.      *
  537.      * If the input is not a word but a single character, then it can be:
  538.      * 1. A special character, in which case we output its code.
  539.      * 2. A blank character in which case we keep getting more characters
  540.      *    to see howmany blanks we get. At the first non blank character,
  541.      *    we output a sequence of special characters which encode multiple
  542.      *    blanks (note: blanks can be spaces, tabs or newlines).
  543.      *
  544.      * Please refer to the state diagram for explanations.
  545.      * I've used gotos since the termination condition is too complex.
  546.      */
  547.  
  548. real_tgetword:
  549.     curlen = 0;
  550.     curword[0] = '\0';
  551.  
  552. concocted_tgetword:
  553.     c = tgetword(infp, inbuf, maxinlen, &inlen, curword, &curlen);
  554.  
  555. bypass_tgetword:
  556.     if (curlen == 0) { /* only one character read and that is in c. */
  557.         switch(c)
  558.         {
  559.         case ' ':
  560.         case '\t':
  561.         case '\n':
  562.             POST_VERBATIM(verbatim_state);    /* need post-verbatim since there might be a LOT of blanks, etc. */
  563.             ret = skip(infp, inbuf, maxinlen, &inlen, c, &skiplen);
  564.             process_spaces(c, skiplen);
  565.             if ((c == '\n') && untilnewline) return outlen;
  566.             if (isalnum(ret)) {
  567.                 curword[0] = (unsigned char)ret;
  568.                 curword[1] = '\0';
  569.                 curlen = 1;
  570.                 goto concocted_tgetword;
  571.             }
  572.             else if (ret != MYEOF) {
  573.                 c = (unsigned int)ret;
  574.                 goto bypass_tgetword;
  575.             }
  576.             /* else fall thru */
  577.  
  578.         case MYEOF: return outlen;
  579.  
  580.         default:
  581.             if ((ret = get_special_text_index(c)) != -1) {
  582.                 if ((maxoutlen >= 0) && (outlen + 1 >= maxoutlen)) return outlen;
  583.                 if (verbatim_state) {    /* no need to do post-verbatim since only one character: optimization */
  584.                     if (outfp != NULL) putc(c, outfp);
  585.                     if (outbuf != NULL) outbuf[outlen] = c;
  586.                     outlen ++;
  587.                 }
  588.                 else {
  589.                     if (outfp != NULL) putc(ret + BEGIN_SPECIAL_TEXTS, outfp);
  590.                     if (outbuf != NULL) outbuf[outlen] = ret + BEGIN_SPECIAL_TEXTS;
  591.                     outlen ++;
  592.                 }
  593.             }
  594.             else {
  595.                 /*
  596.                  * Has to be verbatim character: they have a ONE_VERBATIM before each
  597.                  * irrespective of verbatim_state. Otherwise there is no way to differentiate
  598.                  * one of our special characters from the same characters appearing in the
  599.                  * source. Hence binary files blow-up to twice their original size.
  600.                  * 
  601.                  * Also, if it is a verbatim character that cannot be confused with one of OUR
  602.                  * special characters, then just put it in w/o changing verbatim state. Else
  603.                  * put a begin-verbatim before it and THEN output that character=saves 1 char.
  604.                  */
  605.                 if ((c != BEGIN_VERBATIM) && (c != END_VERBATIM)) {    /* reduces to below if easysearch */
  606.                     EASY_PRE_VERBATIM(verbatim_state)
  607.                     if ((maxoutlen >= 0) && (outlen + 1 >= maxoutlen)) return outlen;
  608.                     if (outfp != NULL) putc(c, outfp);
  609.                     if (outbuf != NULL) outbuf[outlen] = c;
  610.                     outlen ++;
  611.                 }
  612.                 else {    /* like \ escape in C: \ is \\ */
  613.                     if ((maxoutlen >= 0) && (outlen + 2 >= maxoutlen)) return outlen;
  614.                     if (outfp != NULL) putc(ONE_VERBATIM, outfp);
  615.                     if (outbuf != NULL) outbuf[outlen] = ONE_VERBATIM;
  616.                     outlen ++;
  617.                     if (outfp != NULL) putc(c, outfp);
  618.                     if (outbuf != NULL) outbuf[outlen] = c;
  619.                     outlen ++;
  620.                 }
  621.             }
  622.             goto real_tgetword;
  623.         }
  624.     }
  625.     else    /* curlen >= 1 */
  626.     {
  627.         if (!easysearch && verbatim_state && (curlen <= 2)) {
  628.             fprintf(outfp, "%s", curword);    /* don't bother to close the verbatim state and put a 2byte index=saves 1 char */
  629.             curword[0] = '\0';
  630.             curlen = 0;
  631.             goto bypass_tgetword;
  632.         }
  633.         else
  634.         {
  635.             if ((ret = get_special_word_index(curword, curlen)) != -1) {
  636.                 POST_VERBATIM(verbatim_state);
  637.                 /* printf("ret=%d word=%s\n", ret, curword); */
  638.                 if ((maxoutlen >= 0) && (outlen + 1 >= maxoutlen)) return outlen;
  639.                 if (outfp != NULL) putc(ret + BEGIN_SPECIAL_WORDS, outfp);
  640.                 if (outbuf != NULL) outbuf[outlen] = ret + BEGIN_SPECIAL_WORDS;
  641.                 outlen ++;
  642.             }
  643.             else if ((e = get_hash(compress_hash_table, curword, curlen, &hashindex)) != NULL) {
  644. #if    0
  645.                 fprintf(stderr, "%x ", e->val.attribute.index);
  646. #endif    /*0*/
  647.                 encodedindex = encode_index(e->val.attribute.index);
  648.                 POST_VERBATIM(verbatim_state);
  649.                 if ((maxoutlen >= 0) && (outlen + sizeof(short) >= maxoutlen)) return outlen;
  650.                 if (outfp != NULL) {
  651.                     putc(((encodedindex & 0xff00)>>8), outfp);
  652.                     putc((encodedindex & 0x00ff), outfp);
  653.                 }
  654.                 if (outbuf != NULL) {
  655.                     outbuf[outlen] = ((encodedindex & 0xff00)>>8);
  656.                     outbuf[outlen + 1] = encodedindex & 0x00ff;
  657.                 }
  658.                 outlen += sizeof(short);
  659.             }
  660.             else goto NOT_IN_DICTIONARY;
  661.  
  662.         /* process_char_after_word: */
  663.             switch(c)
  664.             {
  665.             case ' ':
  666.                 goto real_tgetword;    /* blank is a part of the word */
  667.  
  668.             case MYEOF:
  669.                 if (easysearch) return outlen;
  670.                 if (outfp != NULL) putc(NOTBLANK, outfp);
  671.                 if (outbuf != NULL) outbuf[outlen] = NOTBLANK;
  672.                 outlen ++;
  673.                 return outlen;
  674.  
  675.             default:
  676.                 if ((maxoutlen >= 0) && (outlen + 1 >= maxoutlen)) return outlen;
  677.                 if ((ret = get_special_delimiter_index(c)) != -1) {
  678.                     if (outfp != NULL) putc((ret+BEGIN_SPECIAL_DELIMITERS), outfp);
  679.                     if (outbuf != NULL) outbuf[outlen] = ret + BEGIN_SPECIAL_DELIMITERS;
  680.                     outlen ++;
  681.                     goto real_tgetword;
  682.                 }
  683.                 else {
  684.                     if (outfp != NULL) putc(NOTBLANK, outfp);
  685.                     if (outbuf != NULL) outbuf[outlen] = NOTBLANK;
  686.                     outlen ++;
  687.                     if (!isalnum(c)) {
  688.                         curword[0] = '\0';
  689.                         curlen = 0;
  690.                         goto bypass_tgetword;
  691.                     }
  692.                     else {    /* might be a number which ended with an alphabet: ".. born in 1992AD" */
  693.                         curword[0] = c;
  694.                         curword[1] = '\0';
  695.                         curlen = 1;
  696.                         goto concocted_tgetword;
  697.                     }
  698.                 }
  699.             }
  700.         }
  701.  
  702.     NOT_IN_DICTIONARY: /* word not in dictionary */
  703.         PRE_VERBATIM(verbatim_state);
  704.         if ((maxoutlen >= 0) && (outlen + curlen >= maxoutlen)) return outlen;
  705.         if ((outfp != NULL) && (0 == fwrite(curword, sizeof(char), curlen, outfp))) return 0;
  706.         if (outbuf != NULL) memcpy(outbuf+outlen, curword, curlen);
  707.         outlen += curlen;
  708.         EASY_POST_VERBATIM(verbatim_state);
  709.  
  710.         switch(c)
  711.         {
  712.         case MYEOF: /* Prefix searches still work since our scheme is context free */ return outlen;
  713.  
  714.         default:
  715.             if (!isalnum(c)) {
  716.                 curword[0] = '\0';
  717.                 curlen = 0;
  718.                 goto bypass_tgetword;
  719.             }
  720.             else {    /* might be a number which ended with an alphabet: ".. born in 1992AD" */
  721.                 curword[0] = c;
  722.                 curword[1] = '\0';
  723.                 curlen = 1;
  724.                 goto concocted_tgetword;
  725.             }
  726.         }
  727.     }
  728. }
  729.  
  730. #define FUNCTION    tcompress_file
  731. #define DIRECTORY    tcompress_directory
  732. #include "trecursive.c"
  733.  
  734. /* returns #bytes (>=0) in the compressed file, -1 if major error (not able to compress) */
  735. tcompress_file(name, outname, flags)
  736.     char    *name, *outname;
  737.     int    flags;
  738. {
  739.     FILE    *fp;
  740.     FILE    *outfp;
  741.     int    inlen, ret;
  742.     struct stat statbuf;
  743.     /* struct timeval tvp[2]; */
  744.     struct utimbuf tvp;
  745.  
  746.     if (name == NULL) return -1;
  747.     if (-1 == stat(name, &statbuf)) {
  748.         if (flags & TC_ERRORMSGS)
  749.             fprintf(stderr, "permission denied or non-existent: %s\n", name);
  750.         return -1;
  751.     }
  752.     if (S_ISDIR(statbuf.st_mode)) {
  753.         if (flags & TC_RECURSIVE) return tcompress_directory(name, outname, flags);
  754.         if (flags & TC_ERRORMSGS)
  755.             fprintf(stderr, "skipping directory: %s\n", name);
  756.         return -1;
  757.     }
  758.     if (!S_ISREG(statbuf.st_mode)) {
  759.         if (flags & TC_ERRORMSGS)
  760.             fprintf(stderr, "not a regular file, skipping: %s\n", name);
  761.         return -1;
  762.     }
  763.     if ((fp = fopen(name, "r")) == NULL) {
  764.         if (flags & TC_ERRORMSGS)
  765.             fprintf(stderr, "permission denied or non-existent: %s\n", name);
  766.         return -1;
  767.     }
  768.     if (!tcompressible_fp(fp, flags)) {
  769.         if (flags & TC_ERRORMSGS)
  770.             fprintf(stderr, " skipping: %s\n", name);
  771.         fclose(fp);
  772.         return -1;
  773.     }
  774.     rewind(fp);
  775.     if (flags & TC_SILENT) {
  776.         printf("%s\n", name);
  777.         fclose(fp);
  778.         return 0;
  779.     }
  780.  
  781.     /* Create and open output file */
  782.     inlen = strlen(name);
  783.     strncpy(outname, name, MAX_LINE_LEN);
  784.     if (inlen + strlen(COMP_SUFFIX) + 1 >= MAX_LINE_LEN) {
  785.         outname[MAX_LINE_LEN - strlen(COMP_SUFFIX)] = '\0';
  786.         fprintf(stderr, "very long file name %s: truncating to: %s", name, outname);
  787.     }
  788.     strcat(outname, COMP_SUFFIX);
  789.     if (!access(outname, R_OK)) {    /* output file exists */
  790.         if (!(flags & TC_OVERWRITE)) {
  791.             fclose(fp);
  792.             return 0;
  793.         }
  794.         else if (!(flags & TC_NOPROMPT)) {
  795.             char    s[8];
  796.             printf("overwrite %s? (y/n): ", outname);
  797.             scanf("%c", s);
  798.             if (s[0] != 'y') {
  799.                 fclose(fp);
  800.                 return 0;
  801.             }
  802.         }
  803.     }
  804.     if ((outfp = fopen(outname, "w")) == NULL) {
  805.         fprintf(stderr, "cannot open for writing: %s\n", outname);
  806.         fclose(fp);
  807.         return -1;
  808.     }
  809.  
  810.     ret = tcompress(fp, -1, outfp, -1, flags);
  811.     if ((statbuf.st_size * (100 - COMP_ATLEAST))/100 < ret) {
  812.         fprintf(stderr, "less than %d%% compression, skipping: %s\n", COMP_ATLEAST, name);
  813.         fclose(fp);
  814.         rewind(outfp);
  815.         fclose(outfp);
  816.         unlink(outname);
  817.         return ret;
  818.     }
  819.  
  820.     if ((ret > 0) && (flags & TC_REMOVE)) unlink(name);
  821.     fclose(fp);
  822.     fflush(outfp);
  823.     fclose(outfp);
  824.     /*
  825.     tvp[0].tv_sec = statbuf.st_atime;
  826.     tvp[0].tv_usec = 0;
  827.     tvp[1].tv_sec = statbuf.st_mtime;
  828.     tvp[1].tv_usec = 0;
  829.     utimes(outname, tvp);
  830.     */
  831.     tvp.actime = statbuf.st_atime;
  832.     tvp.modtime = statbuf.st_mtime;
  833.     utime(outname, &tvp);
  834.     return ret;
  835. }
  836.